﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Art;

namespace System.Windows.Elements
{
    /// <summary>
    /// Interaction logic for ColorDialog.xaml
    /// </summary>
    public sealed partial class ColorDialog : Window
    {
        private static readonly DependencyProperty ActiveChannelProperty;
        private readonly Color currentColor;

        static ColorDialog()
        {
            FrameworkPropertyMetadata metadata;
            
            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = ColorSpaces.Hsv.Hue;
            metadata.PropertyChangedCallback = new PropertyChangedCallback(ColorDialog.OnActiveChannelChanged);
            metadata.CoerceValueCallback = (d, o) =>
                {
                    if (o == null) return ((ColorDialog)d).ActiveChannel;
                    else return o;
                };
            ColorDialog.ActiveChannelProperty = DependencyProperty.Register("ActiveChannel", typeof(ColorChannel), typeof(ColorDialog), metadata);
        }

        static void OnActiveChannelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var colorDialog = (ColorDialog)d;
            var oldChannel = (ColorChannel)e.OldValue;
            var newChannel = (ColorChannel)e.NewValue;

            Boolean isAlpha = newChannel == ColorSpaces.Rgb.Alpha;
            colorDialog.colorBar.IsAlphaDisplayed = isAlpha;
            
            if (isAlpha)
            {
                
                var alphaValue = colorDialog.colorPicker.Accumulator.GetValue(newChannel);
                var redAnchor = new Point(0.1, 0.1);
                var greenAnchor = new Point(0.9, 0.1);
                var blueAnchor = new Point(0.5, 0.9);
                colorDialog.palette.Mixer = z =>
                {
                    var dr = (redAnchor - z).Length;
                    var dg = (greenAnchor - z).Length;
                    var db = (blueAnchor - z).Length;
                    dr = (dr > 1) ? 0 : (1 - dr) * (1 - dr);
                    dg = (dg > 1) ? 0 : (1 - dg) * (1 - dg);
                    db = (db > 1) ? 0 : (1 - db) * (1 - db);
                    return RgbColor.FromScRgb(alphaValue, dr, dg, db);
                };
            }

            var color = colorDialog.colorPicker.Color;
            colorDialog.InvalidatePalettes(color, color, oldChannel, newChannel);
        }

        public ColorDialog()
            : this(Colors.Black)
        {
        }

        public ColorDialog(Color currentColor)
        {
            this.currentColor = currentColor;
            this.InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            this.ActiveChannel = ColorSpaces.Hsv.Hue;
            this.colorPicker.Color = currentColor.ToHsv();
            
        }

        private void NumericBoxLoadedHandler(Object sender, RoutedEventArgs e)
        {
            var numericBox = (NumericBox)e.Source;

            Binding binding = new Binding("Color");
            binding.Source = this.colorPicker;
            binding.Mode = BindingMode.TwoWay;
            binding.Converter = new ClassicChannelSplitter((ColorChannel)numericBox.Tag, this.colorPicker.Accumulator);

            numericBox.SetBinding(NumericBox.ValueProperty, binding);
        }

        private void OKButtonHandler(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }

        private void ColorChangedHandler(object sender, RoutedPropertyChangedEventArgs<IColor> e)
        {
            this.InvalidatePalettes(e.OldValue, e.NewValue, this.ActiveChannel, this.ActiveChannel);
        }

        private void InvalidatePalettes(IColor oldColor, IColor newColor, ColorChannel oldChannel, ColorChannel newChannel)
        {
            RgbColor rgbColor = ColorSpaces.Translate(newColor, ColorSpaces.Rgb);
            HsvColor hsvColor = ColorSpaces.Translate(newColor, ColorSpaces.Hsv);
            LabColor labColor = ColorSpaces.Translate(newColor, ColorSpaces.Lab);

            var alphaValue = rgbColor.ScAlpha;
            var fixedChannelValue = this.colorPicker.Accumulator.GetValue(newChannel);
            
            if (newChannel == StaticHelper.ccRgbA)
            {
                this.colorBar.Mixer = z => RgbColor.FromScRgb(1 - z.Y, rgbColor.ScRed, rgbColor.ScGreen, rgbColor.ScBlue);
                this.colorBar.InverseMixer = c => { var rgb = ColorSpaces.Translate(c, ColorSpaces.Rgb); return new Point(0.5, 1 - rgb.ScAlpha); };
            }
            else if (newChannel == StaticHelper.ccRgbR)
            {
                this.colorBar.Mixer = z => RgbColor.FromScRgb(alphaValue, 1 - z.Y, rgbColor.ScGreen, rgbColor.ScBlue);
                this.colorBar.InverseMixer = c => { var rgb = ColorSpaces.Translate(c, ColorSpaces.Rgb); return new Point(0.5, 1 - rgb.ScRed); };
                
                this.palette.Mixer = z => RgbColor.FromScRgb(alphaValue, fixedChannelValue, 1 - z.Y, z.X);
                this.palette.InverseMixer = c => { var rgb = ColorSpaces.Translate(c, ColorSpaces.Rgb); return new Point(rgb.ScBlue, 1 - rgb.ScGreen); };
            }
            else if (newChannel == StaticHelper.ccRgbG)
            {
                this.colorBar.Mixer = z => RgbColor.FromScRgb(alphaValue, rgbColor.ScRed, 1 - z.Y, rgbColor.ScBlue);
                this.colorBar.InverseMixer = c => { var rgb = ColorSpaces.Translate(c, ColorSpaces.Rgb); return new Point(0.5, 1 - rgb.ScGreen); };

                this.palette.Mixer = z => RgbColor.FromScRgb(alphaValue, 1 - z.Y, fixedChannelValue, z.X);
                this.palette.InverseMixer = c => { var rgb = ColorSpaces.Translate(c, ColorSpaces.Rgb); return new Point(rgb.ScBlue, 1 - rgb.ScRed); };
            }
            else if (newChannel == StaticHelper.ccRgbB)
            {
                this.colorBar.Mixer = z => RgbColor.FromScRgb(alphaValue, rgbColor.ScRed, rgbColor.ScGreen, 1 - z.Y);
                this.colorBar.InverseMixer = c => { var rgb = ColorSpaces.Translate(c, ColorSpaces.Rgb); return new Point(0.5, 1 - rgb.ScBlue); };

                this.palette.Mixer = z => RgbColor.FromScRgb(alphaValue, z.X, 1 - z.Y, fixedChannelValue);
                this.palette.InverseMixer = c => { var rgb = ColorSpaces.Translate(c, ColorSpaces.Rgb); return new Point(rgb.ScRed, 1 - rgb.ScGreen); };
            }
            else if (newChannel == StaticHelper.ccHsvH)
            {
                //this.colorBar.Mixer = z => HsvColor.FromScHsv(alphaValue, 1 - z.Y, 1.0, 1.0); // photoshop
                this.colorBar.Mixer = z => HsvColor.FromScHsv(alphaValue, 0.9999 * (1 - z.Y), hsvColor.ScSaturation, hsvColor.ScValue); // 0.999 is for the color bar not to jump to the bottom on 360
                this.colorBar.InverseMixer = c => { var hsv = ColorSpaces.Translate(c, ColorSpaces.Hsv); return new Point(0.5, 1 - hsv.ScHue); };

                this.palette.Mixer = z => HsvColor.FromScHsv(alphaValue, fixedChannelValue, z.X, 1 - z.Y);
                this.palette.InverseMixer = c => { var hsv = ColorSpaces.Translate(c, ColorSpaces.Hsv); return new Point(hsv.ScSaturation, 1 - hsv.ScValue); };
            }
            else if (newChannel == StaticHelper.ccHsvS)
            {
                this.colorBar.Mixer = z => HsvColor.FromScHsv(alphaValue, hsvColor.ScHue, 1 - z.Y, hsvColor.ScValue);
                this.colorBar.InverseMixer = c => { var hsv = ColorSpaces.Translate(c, ColorSpaces.Hsv); return new Point(0.5, 1 - hsv.ScSaturation); };

                this.palette.Mixer = z => HsvColor.FromScHsv(alphaValue, z.X, fixedChannelValue, 1 - z.Y);
                this.palette.InverseMixer = c => { var hsv = ColorSpaces.Translate(c, ColorSpaces.Hsv); return new Point(hsv.ScHue, 1 - hsv.ScValue); };
            }
            else if (newChannel == StaticHelper.ccHsvV)
            {
                this.colorBar.Mixer = z => HsvColor.FromScHsv(alphaValue, hsvColor.ScHue, hsvColor.ScSaturation, 1 - z.Y);
                this.colorBar.InverseMixer = c => { var hsv = ColorSpaces.Translate(c, ColorSpaces.Hsv); return new Point(0.5, 1 - hsv.ScValue); };

                this.palette.Mixer = z => HsvColor.FromScHsv(alphaValue, z.X, 1 - z.Y, fixedChannelValue);
                this.palette.InverseMixer = c => { var hsv = ColorSpaces.Translate(c, ColorSpaces.Hsv); return new Point(hsv.ScHue, 1 - hsv.ScSaturation); };
            }
            else if (newChannel == StaticHelper.ccLabL)
            {
                this.colorBar.Mixer = z => LabColor.FromScLab(alphaValue, 1 - z.Y, labColor.ScA, labColor.ScB);
                this.colorBar.InverseMixer = c => { var lab = ColorSpaces.Translate(c, ColorSpaces.Lab); return new Point(0.5, 1 - lab.ScLightness); };

                this.palette.Mixer = z => LabColor.FromScLab(alphaValue, fixedChannelValue, z.X - 0.5, 0.5 - z.Y);
                this.palette.InverseMixer = c => { var lab = ColorSpaces.Translate(c, ColorSpaces.Lab); return new Point(lab.ScA + 0.5, 0.5 - lab.ScB); };
            }
            else if (newChannel == StaticHelper.ccLabA)
            {
                this.colorBar.Mixer = z => LabColor.FromScLab(alphaValue, labColor.ScLightness, 0.5 - z.Y, labColor.ScB);
                this.colorBar.InverseMixer = c => { var lab = ColorSpaces.Translate(c, ColorSpaces.Lab); return new Point(0.5, 0.5 - lab.ScA); };

                this.palette.Mixer = z => LabColor.FromScLab(alphaValue, 1 - z.Y, fixedChannelValue, z.X - 0.5);
                this.palette.InverseMixer = c => { var lab = ColorSpaces.Translate(c, ColorSpaces.Lab); return new Point(lab.ScB + 0.5, 1 - lab.ScLightness); };
            }
            else if (newChannel == StaticHelper.ccLabB)
            {
                this.colorBar.Mixer = z => LabColor.FromScLab(alphaValue, labColor.ScLightness, labColor.ScA, 0.5 - z.Y);
                this.colorBar.InverseMixer = c => { var lab = ColorSpaces.Translate(c, ColorSpaces.Lab); return new Point(0.5, 0.5 - lab.ScB); };

                this.palette.Mixer = z => LabColor.FromScLab(alphaValue, 1 - z.Y, z.X - 0.5, fixedChannelValue);
                this.palette.InverseMixer = c => { var lab = ColorSpaces.Translate(c, ColorSpaces.Lab); return new Point(lab.ScA + 0.5, 1 - lab.ScLightness); };
            }
        }

        public Color CurrentColor
        {
            get { return this.currentColor; }
        }

        public Color Color
        {
            get { return this.colorPicker.Color.ToColor(); }
        }

        private ColorChannel ActiveChannel
        {
            get { return (ColorChannel)this.GetValue(ColorDialog.ActiveChannelProperty); }
            set { this.SetValue(ColorDialog.ActiveChannelProperty, value); }
        }
    }
}
